package net.ermannofranco.genericdaojdbc.clause;

import java.beans.PropertyDescriptor;
import java.util.Map;

import net.ermannofranco.genericdaojdbc.dao.GenericDao;
import net.ermannofranco.genericdaojdbc.util.Util;

import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;

/**
 * Conveniene class to build global sql token and SqlParameterSource as needed.
 * 
 * @author ermanno
 * 
 */
public class ClauseBuilder {

	private final Map<String, String> columnMappings;
	private final MapSqlParameterSource source = new MapSqlParameterSource();
	private final PlaceholderBuilder pb = new PlaceholderBuilder();
	private final String sqlToken;

	public ClauseBuilder(Map<String, String> columnMappings, Clause... clauses) {
		this.columnMappings = columnMappings;
		sqlToken = buildSqlAndSource(clauses);
	}

	/**
	 * All first level clauses are assembled with AND token.
	 * 
	 * @param clauses2
	 * @return
	 */
	private synchronized String buildSqlAndSource(Clause... clauses2) {
		StringBuilder sb = new StringBuilder();
		for (Clause c : clauses2) {
			if (c instanceof MultiClause) {
				MultiClause mc = (MultiClause) c;
				sb.append(buildRecursively(mc));
			} else if (c instanceof OneClause) {
				OneClause oc = (OneClause) c;
				sb.append(buildFromOneClause(oc));
			} else {
				throw new IllegalArgumentException(c
						+ ": clause non supportata");
			}
			// !!!!!!
			sb.append(GenericDao.AND);
		}
		sb.setLength(sb.length() - GenericDao.AND.length());
		return sb.toString();
	}

	private synchronized String buildFromOneClause(OneClause c) {
		String propName = c.getPropName();
		String colName = columnMappings.get(propName);
//		if (colName == null)
//			colName = Util.toUP(propName);
		StringBuilder sb = new StringBuilder(GenericDao.BRACKET_OPEN);
		sb.append(colName).append(c.getDiscriminator());
		if (c.requiresNamedParam()) {
			String placeholder = pb.getPlaceholder(propName);
			source.addValue(placeholder, c.getValue());
			sb.append(GenericDao.COLON).append(placeholder);
		}
		sb.append(GenericDao.BRACKET_CLOSED);
		return sb.toString();
	}

	private synchronized String buildRecursively(MultiClause mc) {
		StringBuilder sb = new StringBuilder(GenericDao.BRACKET_OPEN);
		Clause[] innerClauses = mc.getInnerClauses();
		for (int i = 0; i < innerClauses.length; i++) {
			sb.append(buildSqlAndSource(innerClauses[i]));
			sb.append(mc.getConcat());
		}
		sb.setLength(sb.length() - mc.getConcat().length());
		sb.append(GenericDao.BRACKET_CLOSED);
		return sb.toString();
	}

	public String getClauseAsString() {
		return sqlToken;
	}

	public MapSqlParameterSource getParameterSource() {
		return source;
	}

}
